﻿using System;
using System.Collections.Generic;
using MugenInjection.Delegates;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;
using MugenInjection.Interface.Components;

// ReSharper disable CheckNamespace
namespace MugenInjection.Core.Components
// ReSharper restore CheckNamespace
{
    /// <summary>
    /// The base class for components that manages behaviors.
    /// </summary>
    public class BehaviorManagerComponent : Component, IBehaviorManagerComponent
    {
        #region Fields

        private readonly CacheCollection<Type, List<Func<IInjector, object>>> _behaviors;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="BehaviorManagerComponent"/> class.
        /// </summary>
        public BehaviorManagerComponent()
            : this(new CacheCollection<Type, List<Func<IInjector, object>>>())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BehaviorManagerComponent"/> class.
        /// </summary>
        private BehaviorManagerComponent(CacheCollection<Type, List<Func<IInjector, object>>> behaviors)
        {
            Validate.ArgumentNotNull(behaviors, "behaviors");
            _behaviors = behaviors;
        }

        #endregion

        #region Overrides of Component

        /// <summary>
        /// Indicates whether the component is unique.
        /// </summary>
        public override sealed bool IsSingle
        {
            get { return true; }
        }

        /// <summary>
        /// Gets the component type. Need to find a component in the container.
        /// </summary>
        public override sealed Type ComponentType
        {
            get { return typeof(IBehaviorManagerComponent); }
        }

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public override IComponent Clone()
        {
            var behaviors = new CacheCollection<Type, List<Func<IInjector, object>>>();
            if (CopyBehavior)
            {
                lock (_behaviors)
                {
                    foreach (var behavior in _behaviors.Cache)
                    {
                        var list = new List<Func<IInjector, object>>();
                        behaviors[behavior.Key] = list;
                        foreach (var func in behavior.Value)
                            list.Add(func);
                    }
                }
            }
            return new BehaviorManagerComponent(behaviors) { CopyBehavior = CopyBehavior };
        }

        #endregion

        #region Implementation of IBehaviorManagerComponent

        /// <summary>
        /// Indicates that the behaviors will be copy after call method clone.
        /// </summary>
        public bool CopyBehavior { get; set; }

        /// <summary>
        /// Tries get the behavior with the specified <see cref="Type"/>.
        /// </summary>
        /// <typeparam name="TType">The specified <see cref="Type"/>.</typeparam>
        /// <param name="behavior">An instance of behavior.</param>
        /// <returns><c>True</c> if the behavior found; otherwise, <c>false</c>.</returns>
        public bool TryGet<TType>(out TType behavior) where TType : class
        {
            lock (_behaviors)
            {
                behavior = null;
                List<Func<IInjector, object>> behaviors = _behaviors[typeof(TType)];
                if (behaviors == null || behaviors.Count == 0)
                    return false;
                behavior = (TType)behaviors[0](Injector);
                return true;
            }
        }

        /// <summary>
        /// Gets the behavior with the specified <see cref="Type"/>.
        /// </summary>
        /// <typeparam name="TType">The specified <see cref="Type"/>.</typeparam>
        /// <returns>An instance of behavior.</returns>
        public virtual TType Get<TType>() where TType : class
        {
            lock (_behaviors)
            {
                List<Func<IInjector, object>> behaviors = _behaviors[typeof(TType)];
                if (behaviors == null || behaviors.Count == 0)
                    throw new KeyNotFoundException(string.Format("Behavior of the type {0} is not registered.",
                                                                 InjectorUtilsInternal.FormatType(typeof(TType))));
                return (TType)behaviors[0](Injector);
            }
        }

        /// <summary>
        /// Gets all behaviors with the specified <see cref="Type"/>.
        /// </summary>
        /// <typeparam name="TType">The specified <see cref="Type"/>.</typeparam>
        /// <returns>An instances of behavior.</returns>
        public virtual IList<TType> GetAll<TType>() where TType : class
        {
            lock (_behaviors)
            {
                List<Func<IInjector, object>> behaviors = _behaviors[typeof(TType)];
                if (behaviors == null || behaviors.Count == 0)
                    throw new KeyNotFoundException(string.Format("Behavior of the type {0} is not registered.",
                                                                 InjectorUtilsInternal.FormatType(typeof(TType))));
                var result = new List<TType>();
                foreach (var behavior in behaviors)
                {
                    result.Add((TType)behavior(Injector));
                }
                return result;
            }
        }

        /// <summary>
        /// Indicates that the specified <see cref="Type"/> of behavior exists in the collection.
        /// </summary>
        /// <typeparam name="TType">The specified behavior <see cref="Type"/>.</typeparam>
        /// <returns><c>True</c> if the specified behavior exist; otherwise, <c>false</c>.</returns>
        public virtual bool IsExist<TType>() where TType : class
        {
            lock (_behaviors)
            {
                return _behaviors[typeof(TType)] != null;
            }
        }

        /// <summary>
        /// Adds the specified behavior.
        /// </summary>
        public virtual void Add<TType, TTypeTo>()
            where TType : class
            where TTypeTo : TType
        {
            lock (_behaviors)
            {
                _behaviors
                    .GetOrAdd(typeof(TType), () => new List<Func<IInjector, object>>())
                    .Add(injector => injector.Resolve(new BindingContext(typeof(TTypeTo), null, null, null, injector, null)));
            }
        }

        /// <summary>
        /// Adds the specified behavior.
        /// </summary>
        public virtual void Add<TType>(TType behavior) where TType : class
        {
            Validate.ArgumentNotNull(behavior, "behavior");
            lock (_behaviors)
            {
                _behaviors
                    .GetOrAdd(typeof(TType), () => new List<Func<IInjector, object>>())
                    .Add(injector => behavior);
            }
        }

        /// <summary>
        /// Removes the specified behavior.
        /// </summary>
        public virtual void Remove<TType>() where TType : class
        {
            lock (_behaviors)
            {
                _behaviors.Remove(typeof(TType));
            }
        }

        #endregion
    }
}